Quick Start
Welcome to Scripting, an iOS app that lets you code UI components in TypeScript using React-like TSX syntax. With Scripting, you can create and present iOS utility UI pages through a familiar coding structure, using wrapped SwiftUI views for a smooth, native experience on iOS. This guide walks you through setting up your project, creating components, and working with hooks to build dynamic interfaces.
Table of Contents
- Getting Started
- Creating a Script Project
- Importing Components
- Creating Custom Components
- Presenting UI Views
- Using Hooks
- Building Complex UIs
1. Getting Started
In Scripting, you’ll create simple UI elements by defining them with function components. Every component and API you’ll need can be imported from the scripting
package.
2. Creating a Script Project
Before you begin coding, you need to create a script project. Once the project is set up, write your code in the index.tsx
file. This is your main entry point for defining UI components and logic.
Example setup in index.tsx
:
1import { VStack, Text } from "scripting"
2
3// Define a custom view component
4function View() {
5 return (
6 <VStack>
7 <Text>Hello, Scripting!</Text>
8 </VStack>
9 )
10}
3. Importing Views
All views and some APIs from SwiftUI are wrapped and accessible through the scripting
package. Here’s a list of some available views:
- Layout Views:
VStack
, HStack
, ZStack
, Grid
- Controls:
Button
, Picker
, Toggle
, Slider
, ColorPicker
- Collections:
List
, Section
- Date and Time:
DatePicker
- Text and Labels:
Text
, Label
, TextField
To use these in your project, import them as shown:
1import { VStack, Text, Button, Picker } from "scripting"
4. Creating Custom Components
Function components in Scripting work just like in React, with JSX-like syntax for building reusable components.
Example:
1import { VStack, HStack, Text, Button } from "scripting"
2
3function Greeting({
4 name
5}: {
6 name: string
7}) {
8 return (
9 <HStack>
10 <Text>Hello, {name}!</Text>
11 </HStack>
12 )
13}
14
15function MainView() {
16 return (
17 <VStack>
18 <Greeting name="Scripting User" />
19 <Button
20 title="Click Me"
21 action={() => console.log("Button Clicked!")}
22 />
23 </VStack>
24 )
25}
5. Presenting UI Views
To present a UI view, use the Navigation.present
method. This allows you to display a custom component as a modal view and handle its dismissal. The Navigation.present
method returns a promise that fulfills when the view is dismissed. To avoid memory leaks, always call Script.exit()
after the view is dismissed.
Example:
1import { VStack, Text, Navigation, Script } from "scripting"
2
3function View() {
4 return (
5 <VStack>
6 <Text>Hello, Scripting!</Text>
7 </VStack>
8 )
9}
10
11// Present the view
12Navigation.present({
13 element: <View />
14}).then(() => {
15 // Clean up to avoid memory leaks
16 Script.exit()
17})
In this example, Navigation.present({ element: <View /> })
displays the View
component, and when the user dismisses it, Script.exit()
ensures resources are freed.
6. Using Hooks
Scripting supports a range of React-like hooks for managing state, effects, memoization, and context. Here’s a guide on how to use each hook with examples:
useState
The useState
hook lets you add local state to a function component.
1import { useState, VStack, Text, Button } from "scripting"
2
3function Counter() {
4 const [count, setCount] = useState(0)
5
6 return (
7 <VStack>
8 <Text>Count: {count}</Text>
9 <Button
10 title="Increment"
11 action={() => setCount(count + 1)}
12 />
13 </VStack>
14 )
15}
In this example, clicking the button updates the count
variable, which automatically re-renders the component.
useEffect
The useEffect
hook lets you perform side effects in your components, such as fetching data or setting up subscriptions.
1import { useState, useEffect, VStack, Text } from "scripting"
2
3function TimeDisplay() {
4 const [time, setTime] = useState(
5 new Date().toLocaleTimeString()
6 )
7
8 useEffect(() => {
9 let timerId: number
10
11 const startTimer = () => {
12 timerId = setTimeout(() => {
13 setTime(new Date().toLocaleTimeString())
14 }, 1000)
15 }
16
17 startTimer()
18
19 return () => clearTimeout(timerId) // Clean up on unmount
20 }, [])
21
22 return <Text>Current Time: {time}</Text>
23}
In this example, the useEffect
hook sets up an interval to update the time
variable every second, and clears the interval on component unmount.
useReducer
The useReducer
hook is useful for managing complex state logic in components.
1import { useReducer, VStack, Text, Button } from "scripting"
2
3type Action = {
4 type: "increment"
5} | {
6 type: "decrement"
7}
8const reducer = (state: number, action: Action) => {
9 switch (action.type) {
10 case "increment":
11 return state + 1
12 case "decrement":
13 return state - 1
14 default:
15 return state
16 }
17}
18
19function Counter() {
20 const [count, dispatch] = useReducer(reducer, 0)
21
22 return (
23 <VStack>
24 <Text>Count: {count}</Text>
25 <Button
26 title="Increment"
27 action={() => dispatch({ type: "increment" })}
28 />
29 <Button
30 title="Decrement"
31 action={() => dispatch({ type: "decrement" })}
32 />
33 </VStack>
34 )
35}
The useReducer
hook helps you handle complex state transitions by using a reducer function.
useCallback
The useCallback
hook lets you memoize functions, optimizing performance by preventing unnecessary re-creations of the function on every render.
1import { useState, useCallback, VStack, Text, Button } from "scripting"
2
3function Counter() {
4 const [count, setCount] = useState(0)
5
6 const increment = useCallback(() => {
7 setCount((prev) => prev + 1)
8 }, [])
9
10 return (
11 <VStack>
12 <Text>Count: {count}</Text>
13 <Button
14 title="Increment"
15 action={increment}
16 />
17 </VStack>
18 )
19}
With useCallback
, the increment
function is only re-created when necessary, improving performance in large or frequently updated components.
useMemo
The useMemo
hook lets you memoize values, caching expensive computations for better performance.
1import { useState, useMemo, VStack, Text, Button } from "scripting"
2
3function FactorialCounter() {
4 const [count, setCount] = useState(1)
5
6 const factorial = useMemo(() => {
7 let result = 1
8 for (let i = 1; i <= count; i++) result *= i
9 return result
10 }, [count])
11
12 return (
13 <VStack>
14 <Text>Factorial of {count} is {factorial}</Text>
15 <Button
16 title="Increase"
17 action={() => setCount(count + 1)}
18 />
19 </VStack>
20 )
21}
The useMemo
hook optimizes performance by only re-calculating the factorial when count
changes.
useContext
The useContext
hook allows components to access shared state across the app without prop drilling, using a Context API.
1import { createContext, useContext, VStack, Text, Button } from "scripting"
2
3const CountContext = createContext<number>()
4
5function Display() {
6 const count = useContext(CountContext)
7 return <Text>Shared Count: {count}</Text>
8}
9
10function App() {
11 return (
12 <CountContext.Provider value={42}>
13 <VStack>
14 <Display />
15 </VStack>
16 </CountContext.Provider>
17 )
18}
In this example, useContext
accesses CountContext
to get a shared count value across the app.
7. Building Complex UIs
Combine available views, hooks, and custom components to create complex, fully functional UIs.
Example:
1import { useState, VStack, Text, TextField, List, Section, NavigationStack, Script } from "scripting"
2
3function ToDoApp() {
4 const [tasks, setTasks] = useState(["Task 1", "Task 2", "Task 3"])
5 const [content, setContent] = useState("")
6
7 return (
8 <NavigationStack>
9 <List
10 navigationTitle="My Tasks"
11 >
12 <Section>
13 {tasks.map((task, index) => (
14 <Text key={index}>{task}</Text>
15 ))}
16 </Section>
17
18 <TextField
19 title="New Task"
20 value={content}
21 onChanged={setContent}
22 onSubmit={() => {
23 if (content.length === 0) {
24 return
25 }
26 setTasks([...tasks, content])
27 setContent("")
28 }}
29 />
30 </List>
31 </NavigationStack>
32 )
33}
34
35async function run() {
36 await Navigation.present({
37 element: <ToDoApp />
38 })
39
40 Script.exit()
41}
For further details, check the full API documentation, which includes more examples and use cases for scripting
package components and APIs.